package com.enation.app.javashop.core.aftersale.service.impl;

import com.alibaba.fastjson.JSON;
import com.dag.eagleshop.core.account.model.dto.account.AccountDTO;
import com.dag.eagleshop.core.account.model.dto.account.TransferReqDTO;
import com.dag.eagleshop.core.account.model.dto.account.TransferRespDTO;
import com.dag.eagleshop.core.account.model.enums.TradeSubjectEnum;
import com.dag.eagleshop.core.account.model.enums.TransferTypeEnum;
import com.dag.eagleshop.core.account.service.AccountManager;
import com.enation.app.javashop.core.aftersale.AftersaleErrorCode;
import com.enation.app.javashop.core.aftersale.model.dos.RefundDO;
import com.enation.app.javashop.core.aftersale.model.dos.RefundGoodsDO;
import com.enation.app.javashop.core.aftersale.model.dos.RefundItemDO;
import com.enation.app.javashop.core.aftersale.model.dos.RefundLogDO;
import com.enation.app.javashop.core.aftersale.model.dto.RefundDTO;
import com.enation.app.javashop.core.aftersale.model.dto.RefundDetailDTO;
import com.enation.app.javashop.core.aftersale.model.dto.RefundGoodsDTO;
import com.enation.app.javashop.core.aftersale.model.dto.SpreadRefundDTO;
import com.enation.app.javashop.core.aftersale.model.enums.*;
import com.enation.app.javashop.core.aftersale.model.vo.*;
import com.enation.app.javashop.core.aftersale.service.AfterSaleManager;
import com.enation.app.javashop.core.aftersale.service.RefundOperateChecker;
import com.enation.app.javashop.core.base.message.RefundChangeMsg;
import com.enation.app.javashop.core.base.rabbitmq.AmqpExchange;
import com.enation.app.javashop.core.distribution.service.DistributionManager;
import com.enation.app.javashop.core.goods.model.dos.SpecValuesDO;
import com.enation.app.javashop.core.goods.model.enums.Permission;
import com.enation.app.javashop.core.member.model.dos.Member;
import com.enation.app.javashop.core.member.service.MemberManager;
import com.enation.app.javashop.core.payment.model.dos.PaymentMethodDO;
import com.enation.app.javashop.core.payment.model.enums.PaymentPluginEnum;
import com.enation.app.javashop.core.payment.service.PaymentMethodManager;
import com.enation.app.javashop.core.payment.service.PaymentPluginManager;
import com.enation.app.javashop.core.payment.service.RefundItemManager;
import com.enation.app.javashop.core.payment.service.RefundManager;
import com.enation.app.javashop.core.shop.model.dos.Clerk;
import com.enation.app.javashop.core.shop.service.ClerkManager;
import com.enation.app.javashop.core.trade.cart.model.dos.OrderPermission;
import com.enation.app.javashop.core.trade.order.model.dos.OrderDO;
import com.enation.app.javashop.core.trade.order.model.dos.OrderItemsDO;
import com.enation.app.javashop.core.trade.order.model.dto.RefundOrderDTO;
import com.enation.app.javashop.core.trade.order.model.enums.*;
import com.enation.app.javashop.core.trade.order.model.vo.CancelVO;
import com.enation.app.javashop.core.trade.order.model.vo.OrderDetailVO;
import com.enation.app.javashop.core.trade.order.model.vo.OrderOperateAllowable;
import com.enation.app.javashop.core.trade.order.model.vo.OrderSkuVO;
import com.enation.app.javashop.core.trade.order.service.OrderOperateManager;
import com.enation.app.javashop.core.trade.order.service.OrderQueryManager;
import com.enation.app.javashop.core.trade.sdk.model.OrderDetailDTO;
import com.enation.app.javashop.core.trade.sdk.model.OrderSkuDTO;
import com.enation.app.javashop.framework.context.AdminUserContext;
import com.enation.app.javashop.framework.context.UserContext;
import com.enation.app.javashop.framework.database.DaoSupport;
import com.enation.app.javashop.framework.database.Page;
import com.enation.app.javashop.framework.exception.ServiceException;
import com.enation.app.javashop.framework.security.model.Admin;
import com.enation.app.javashop.framework.security.model.Buyer;
import com.enation.app.javashop.framework.security.model.Seller;
import com.enation.app.javashop.framework.util.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.HashedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * @author zjp
 * @version v7.0
 * @Description 售后管理业务类
 * @ClassName AfterSaleManagerImpl
 * @since v7.0 上午11:32 2018/5/8
 */
@Service
public class AfterSaleManagerImpl implements AfterSaleManager {


    @Autowired
    @Qualifier("tradeDaoSupport")
    private DaoSupport daoSupport;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Autowired
    private OrderQueryManager orderQueryManager;

    @Autowired
    private OrderOperateManager orderOperateManager;

    @Autowired
    private RefundManager refundManager;

    @Autowired
    private RefundItemManager refundItemManager;

    @Autowired
    private PaymentMethodManager paymentMethodManager;

    @Autowired
    private DistributionManager distributionManager;

    @Autowired
    private AccountManager accountManager;

    @Autowired
    private List<PaymentPluginManager> paymentPluginList;

    @Autowired
    private MemberManager memberManager;

    @Autowired
    private ClerkManager clerkManager;

    /**
     * 日志记录
     */
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void applyRefund(BuyerRefundApplyVO refundApply) {
        refundApply.setRefuseType(RefuseTypeEnum.RETURN_MONEY.value());
        RefundDO refund = this.buyerRefund(refundApply);
        this.log(refund.getSn(), refund.getMemberName(), "申请退款");
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void systemRefund(BuyerRefundApplyVO refundApply) {
        refundApply.setRefuseType(RefuseTypeEnum.RETURN_MONEY.value());
        OrderDetailDTO order = orderQueryManager.getModel(refundApply.getOrderSn());
        RefundDO refund = innerRefund(refundApply, order);
        this.log(refund.getSn(), "系统操作", "系统退款");
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void applyGoodsReturn(BuyerRefundApplyVO goodsReturnsApply) {
        goodsReturnsApply.setRefuseType(RefuseTypeEnum.RETURN_GOODS.value());
        RefundDO refund = this.buyerRefund(goodsReturnsApply);
        this.log(refund.getSn(), refund.getMemberName(), "申请退货");
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void cancelOrder(BuyerCancelOrderVO buyerCancelOrderVO) {
        //获取登录会员
        Buyer buyer = UserContext.getBuyer();
        //获取订单信息对订单进行属主校验
        OrderDetailDTO orderDetail = orderQueryManager.getModel(buyerCancelOrderVO.getOrderSn());
        if (orderDetail == null || !buyer.getUid().equals(orderDetail.getMemberId())) {
            throw new ServiceException(AftersaleErrorCode.E604.name(), "订单不存在");
        }
        baseCancel(buyerCancelOrderVO, orderDetail, false);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void sysCancelOrder(BuyerCancelOrderVO buyerCancelOrderVO) {

        OrderDetailDTO orderDetail = orderQueryManager.getModel(buyerCancelOrderVO.getOrderSn());
        //未付款订单直接变成已取消
        if (PayStatusEnum.PAY_NO.value().equals(orderDetail.getPayStatus()) && orderDetail.getOrderStatus()!=OrderStatusEnum.CANCELLED.name()) {
            CancelVO cancelVO = new CancelVO();
            cancelVO.setOrderSn(orderDetail.getSn());
            cancelVO.setOperator("系统自动");
            cancelVO.setReason("活动结束未成团");
            this.orderOperateManager.cancel(cancelVO, OrderPermission.admin);
        } else {
            //申请退款单
            RefundDO refund = baseCancel(buyerCancelOrderVO, orderDetail, true);
            //退款单自动审核通过
            RefundApprovalVO refundApproval = new RefundApprovalVO();
            refundApproval.setAgree(1);
            refundApproval.setRefundPoint(0);
            refundApproval.setSn(refund.getSn());
            refundApproval.setRemark("系统自动审核");
            refundApproval.setRefundPrice(refund.getRefundPrice());
            this.approval(refundApproval, Permission.ADMIN);
        }
    }


    /**
     * 取消订单通用
     * @param buyerCancelOrderVO 取消订单通用VO
     * @param orderDetail 订单详情DTO
     */
    private RefundDO baseCancel(BuyerCancelOrderVO buyerCancelOrderVO, OrderDetailDTO orderDetail, boolean isAdmin) {

        //已付款订单才可执行退款操作
        if (!orderDetail.getOrderStatus().equals(OrderStatusEnum.PAID_OFF.value()) && !orderDetail.getPayStatus().equals(PayStatusEnum.PAY_YES.value())) {
            throw new ServiceException(AftersaleErrorCode.E601.name(), "操作不允许");
        }
        //此订单正在申请售后无法取消订单（申请售后处理完才能取消订单）
        List<RefundItemDO> refundItem = refundItemManager.queryApplyRefundItemByOrderSn(buyerCancelOrderVO.getOrderSn());
        if (!CollectionUtils.isEmpty(refundItem)) {
            //有正在申请的退款单明细
            throw new ServiceException(AftersaleErrorCode.E611.name(), AftersaleErrorCode.E611.describe());
        }
        //此订单已支付，已发货为部分发货的订单，不能申请取消订单
        if (orderDetail.getOrderStatus().equals(OrderStatusEnum.PAID_OFF.value()) && orderDetail.getShipStatus().equals(ShipStatusEnum.SHIP_YES.value())) {
            throw new ServiceException(AftersaleErrorCode.E613.name(), AftersaleErrorCode.E613.describe());
        }
        //订单操作校验
        OrderOperateAllowable orderOperateAllowableVO = orderDetail.getOrderOperateAllowableVO();
        if (!orderOperateAllowableVO.getAllowServiceCancel()) {
            throw new ServiceException(AftersaleErrorCode.E601.name(), "操作不允许");
        }

        String refundSn = DateUtil.toString(DateUtil.getDateline(), "yyMMddHHmmss") + (new Random().nextInt(999) + 100);
        //拼装退款单
        RefundDO refundDO = new RefundDO();
        refundDO.setSn(refundSn);
        refundDO.setCustomerRemark(buyerCancelOrderVO.getCustomerRemark());
        refundDO.setMemberId(orderDetail.getMemberId());
        refundDO.setMemberName(orderDetail.getMemberName());
        refundDO.setSellerId(orderDetail.getSellerId());
        refundDO.setSellerName(orderDetail.getSellerName());
        refundDO.setOrderSn(orderDetail.getSn());
        refundDO.setTradeSn(orderDetail.getTradeSn());
        refundDO.setOrderCreateTime(orderDetail.getCreateTime());
        refundDO.setRefundStatus(RefundStatusEnum.APPLY.name());
        refundDO.setCreateTime(DateUtil.getDateline());
        //获取取消订单时可退款金额（含运费）
        double allowOrderRefundPrice = orderQueryManager.getOrderRefundPrice(orderDetail.getSn());
        Double shippingPrice = orderDetail.getShippingPrice();
        if(shippingPrice != null && shippingPrice > 0){
            allowOrderRefundPrice = CurrencyUtil.add(allowOrderRefundPrice, shippingPrice);
        }
        refundDO.setRefundPrice(allowOrderRefundPrice);


        //判断当前支付方式是否支持原路退回,如果不支持则退款方式不能为空
        PaymentMethodDO paymentMethodDO = paymentMethodManager.getByPluginId(orderDetail.getPaymentPluginId());
        String refundWay = RefundWayEnum.ORIGINAL.name();

        //非原路退回
        if (paymentMethodDO == null || paymentMethodDO.getIsRetrace() == 0) {
            refundWay = RefundWayEnum.OFFLINE.name();
            if (isAdmin) {
                //这里暂时是拼团退款并且是管理员点击确认收款的订单
                refundDO.setAccountType("");
                refundDO.setReturnAccount("");
                refundDO.setBankAccountName("");
                refundDO.setBankAccountNumber("");
                refundDO.setBankDepositName("");
                refundDO.setBankName("");
            } else {
                if (buyerCancelOrderVO.getAccountType() == null) {
                    throw new ServiceException(AftersaleErrorCode.E605.name(), "退款方式必填");
                }
                //银行转账
                if (AccountTypeEnum.BANKTRANSFER.name().equals(buyerCancelOrderVO.getAccountType())) {
                    refundDO.setBankAccountName(buyerCancelOrderVO.getBankAccountName());
                    refundDO.setBankAccountNumber(buyerCancelOrderVO.getBankAccountNumber());
                    refundDO.setBankDepositName(buyerCancelOrderVO.getBankDepositName());
                    refundDO.setBankName(buyerCancelOrderVO.getBankName());
                } else {
                    //支付宝或者微信
                    refundDO.setReturnAccount(buyerCancelOrderVO.getReturnAccount());
                }
                refundDO.setAccountType(buyerCancelOrderVO.getAccountType());
            }

        } else {
            //退款单确定退款账户类型
            this.getAccountType(refundDO, paymentMethodDO);
        }
        refundDO.setRefundWay(refundWay);
        refundDO.setRefundReason(buyerCancelOrderVO.getRefundReason());
        refundDO.setRefundType(RefundTypeEnum.CANCEL_ORDER.name());
        refundDO.setPaymentType(PaymentTypeEnum.ONLINE.name());
        refundDO.setRefuseType(RefuseTypeEnum.RETURN_MONEY.name());
        refundDO.setPayOrderNo(orderDetail.getPayOrderNo());
        this.daoSupport.insert(refundDO);
        refundDO.setId(this.daoSupport.getLastId("es_refund"));

        //根据支付插件确定退款方式和支付时的流水号,生成refund_item，用于向第三方发起退款
        if (!ObjectUtils.isEmpty(paymentMethodDO)) {
            PaymentPluginManager plugin = this.findPlugin(paymentMethodDO.getPluginId());
            if (ObjectUtils.isEmpty(plugin)) {
                throw new ServiceException(AftersaleErrorCode.E610.name(), "退款单创建失败");
            }
            plugin.createRefundItem(refundDO, orderDetail);
        }

        //生成退货商品
        List<OrderSkuDTO> orderSkuList = orderDetail.getOrderSkuList();
        for (OrderSkuDTO orderSkuDTO : orderSkuList) {
            this.refundGoods(orderSkuDTO, orderSkuDTO.getNum(), refundSn);
        }

        //更新订单状态及售后状态
        orderOperateManager.updateOrderServiceStatus(orderDetail.getSn(), ServiceStatusEnum.APPLY.name());

        // 发送申请退款的消息
        RefundChangeMsg refundStatusChangeMessage = new RefundChangeMsg(refundDO, RefundStatusEnum.APPLY);
        amqpTemplate.convertAndSend(AmqpExchange.REFUND_STATUS_CHANGE, AmqpExchange.REFUND_STATUS_CHANGE + "_QUEUE",
                refundStatusChangeMessage);

        // 记录日志
        this.log(refundDO.getSn(), refundDO.getMemberName(), "申请取消订单");
        return refundDO;
    }

    /**
     * 查找支付插件
     * @param pluginId 支付插件ID
     */
    private PaymentPluginManager findPlugin(String pluginId) {
        for (PaymentPluginManager plugin : paymentPluginList) {
            if (plugin.getPluginId().equals(pluginId)) {
                return plugin;
            }
        }
        return null;
    }
    @Override
    public RefundApprovalVO approval(RefundApprovalVO refundApproval, Permission permission) {

        // 实际审核退款
        RefundDO refund = this.daoSupport.queryForObject("select * from es_refund where sn =?", RefundDTO.class, refundApproval.getSn());
        RefundApprovalVO refundApprovalVO = this.innerApproval(refundApproval, permission, refund);

        // 发送审核通过或者拒绝的消息(将发送消息提取出实际审核退款方法中，保证实际审核退款的事务提交后再发送mq)
        RefundChangeMsg refundStatusChangeMessage = refundApproval.getAgree().equals(1) ? new RefundChangeMsg(refund, RefundStatusEnum.PASS) : new RefundChangeMsg(refund, RefundStatusEnum.REFUSE);
        amqpTemplate.convertAndSend(AmqpExchange.REFUND_STATUS_CHANGE, AmqpExchange.REFUND_STATUS_CHANGE + "_QUEUE", refundStatusChangeMessage);

        return refundApprovalVO;
    }

    /**
     * 商家审核退款
     * @param refundApproval 批准 vo
     * @param permission 权限
     * @return 卖家/平台审核退(款)货VO
     */
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public RefundApprovalVO innerApproval(RefundApprovalVO refundApproval, Permission permission, RefundDO refund) {

        String operater = "系统管理员";
        //默认admin不需要验证
        if (Permission.ADMIN.equals(permission)) {
            Admin admin = AdminUserContext.getAdmin();
            if (!ObjectUtils.isEmpty(admin)) {
                operater = admin.getUsername();
            }
        } else {
            Seller seller = UserContext.getSeller();
            Integer uid = seller.getUid();
            Clerk clerk = clerkManager.getClerkByMemberId(uid);
            // 对于不存在或不属于当前店铺的退款单进行校验
            if (refund == null || !refund.getSellerId().equals(seller.getSellerId())) {
                throw new ServiceException(AftersaleErrorCode.E603.name(), "退款单不存在");
            }
            //操作权限校验
            this.checkAllowable(refund, RefundOperateEnum.SELLER_APPROVAL);
            operater = clerk.getClerkName();
        }
        // 审核人
        refund.setAuditName(operater);
        //退款金额校验
        Double agreeRefundPrice = refundApproval.getRefundPrice();
        this.checkMoney(refund, agreeRefundPrice);

        String refundStatus = RefundStatusEnum.REFUSE.value();

        // 判断是否同意退款
        if (refundApproval.getAgree().equals(1)) {
            if (refund.getRefuseType().equals(RefuseTypeEnum.RETURN_MONEY.value())) {
                refundStatus = RefundStatusEnum.WAIT_FOR_MANUAL.value();
            }
            if (refund.getRefuseType().equals(RefuseTypeEnum.RETURN_GOODS.value())) {
                refundStatus = RefundStatusEnum.PASS.value();
            }
            refund.setRefundStatus(refundStatus);

            //假如同意的退款金额和申请时的退款金额不同，先删除所有旧的退款单明细，后重算退款详清单中的退款金额
            this.recountRefundItem(refund, agreeRefundPrice);

            // 如果为申请退款且退款方式支持原路退回则审核通过后直接原路退款
            if (refund.getRefuseType().equals(RefuseTypeEnum.RETURN_MONEY.value()) && RefundWayEnum.ORIGINAL.name().equals(refund.getRefundWay()) && agreeRefundPrice > 0) {
                //获取到退款单详情，不同支付方式退款方式不用，循环调用退款返回
                long dateline = DateUtil.getDateline();
                boolean isSuccess = this.batchOriginRefund(refund, dateline);

                if (isSuccess) {
                    refundStatus = RefundStatusEnum.REFUNDING.value();
                    //添加时间：2020年9月30日 13:53:15  解决OrderBillConsumer 订单实际退款生成退款记录时获取不到退款时间报错
                    refund.setRefundTime(dateline);
                    //获取订单可退金额和订单退款后剩余金额，调用更新退款后剩余收益方法
                    this.updateRefundSurplusProfit(refund);

                    this.daoSupport.execute(
                            "update es_refund set refund_status=?,seller_remark=? ,refund_price=? ,refund_point=?,refund_time=?,audit_name = ? where sn =?",
                            refundStatus, refundApproval.getRemark(), refundApproval.getRefundPrice(),
                            refundApproval.getRefundPoint(), refund.getRefundTime(), refund.getAuditName(), refundApproval.getSn());

                    //发送原路退回成功消息
                    RefundChangeMsg refundStatusChangeMessage = new RefundChangeMsg(refund, RefundStatusEnum.REFUNDING);
                    amqpTemplate.convertAndSend(AmqpExchange.REFUND_STATUS_CHANGE, AmqpExchange.REFUND_STATUS_CHANGE + "_QUEUE",
                            refundStatusChangeMessage);
                } else {
                    refundStatus = RefundStatusEnum.REFUNDFAIL.value();
                    this.daoSupport.execute(
                            "update es_refund set refund_status=?,seller_remark=? ,refund_price=? ,refund_point=?,refund_fail_reason=? ,audit_name = ? where sn =?",
                            refundStatus, refundApproval.getRemark(), refundApproval.getRefundPrice(),
                            refundApproval.getRefundPoint(), refund.getRefundFailReason(), refund.getAuditName(), refundApproval.getSn());
                    this.daoSupport.execute(
                            "update es_refund_item set item_status=?, refund_fail_reason=? where refund_sn =?",
                            RefundItemStatusEnum.FAIL.value(), refund.getRefundFailReason(), refundApproval.getSn());
                }
            }
            if (agreeRefundPrice == 0) {
                this.daoSupport.execute(
                        "update es_refund set refund_status=?,seller_remark=? ,refund_price=? ,refund_point=?,refund_time=?,audit_name = ? where sn =?",
                        RefundStatusEnum.COMPLETED.value(), refundApproval.getRemark(), refundApproval.getRefundPrice(),
                        refundApproval.getRefundPoint(), refund.getRefundTime(), refund.getAuditName(), refundApproval.getSn());
                this.daoSupport.execute(
                        "update es_refund_item set item_status=?, refund_price=?, refund_time=? where refund_sn =?",
                        RefundItemStatusEnum.SUCCESS.value(), agreeRefundPrice, refund.getRefundTime(), refundApproval.getSn());
            }
            // 如果为退货且审核通过，修改退款单为申请通过，退款单明细还是申请中，入库操作时，发起退款时还需要使用
            if (refund.getRefuseType().equals(RefuseTypeEnum.RETURN_GOODS.value()) && RefundWayEnum.ORIGINAL.name().equals(refund.getRefundWay())) {
                this.daoSupport.execute(
                        "update es_refund set refund_status=?,seller_remark=? ,refund_price=? ,refund_point=?,audit_name = ? where sn =?",
                        refundStatus, refundApproval.getRemark(), refundApproval.getRefundPrice(),
                        refundApproval.getRefundPoint(), refund.getAuditName(), refundApproval.getSn());
            }
        } else {
            // 审核未通过，修改退款单为已拒绝
            this.daoSupport.execute(
                    "update es_refund set refund_status=?,seller_remark=? ,refund_price=? ,refund_point=?,refund_fail_reason=? ,audit_name = ? where sn =?",
                    refundStatus, refundApproval.getRemark(), refundApproval.getRefundPrice(),
                    refundApproval.getRefundPoint(), refund.getRefundFailReason(), refund.getAuditName(), refundApproval.getSn());
            this.daoSupport.execute(
                    "update es_refund_item set item_status=?, refund_fail_reason=? where refund_sn =?",
                    RefundItemStatusEnum.FAIL.value(), "审核拒绝", refundApproval.getSn());
        }

        // 记录日志
        this.log(refundApproval.getSn(), operater, "审核退货（款），结果为：" + (refundApproval.getAgree() == 1 ? "同意" : "拒绝"));

        return refundApproval;
    }

    /**
     * ***************************************具体退款的方法**********************************************
     * 批量操作退款单明细，进行退款
     */
    private boolean batchOriginRefund(RefundDO refund, long dateline){

        //获取到退款单详情，不同支付方式调用不同的退款方式，循环调用退款返回
        List<RefundItemDO> refundItemList = refundItemManager.queryRefundItemByRefundIdAndStatus(refund.getId(), RefundItemStatusEnum.APPLY.value());

        //解决refund_item表创建之前申请退款时没有数据，导致修改退款单状态错误
        if (CollectionUtils.isEmpty(refundItemList)) {
            refund.setRefundFailReason("找不到退款单明细");
            return false;
        }

        //循环调用具体插件的退款
        for (RefundItemDO refundItem : refundItemList) {
            // **********************************开始退款*********************************************
            Map resultMap = refundManager.originRefund(refundItem.getPayOrderNo(), refundItem.getRefundSn(), refundItem.getRefundPrice());
            // **********************************结束退款*********************************************
            String result = StringUtil.toString(resultMap.get("result"));
            if (Boolean.valueOf(result)) {
                refundItem.setRefundTime(dateline);
                refundItem.setItemStatus(RefundItemStatusEnum.SUCCESS.value());
                //每笔退款单退成都更新可退款金额
                this.changeOrderRefundPrice(refund, refundItem.getRefundPrice());
            } else {
                refundItem.setItemStatus(RefundItemStatusEnum.FAIL.value());
                String failReason = StringUtil.toString(resultMap.get("fail_reason"));
                refundItem.setRefundFailReason(failReason);
                refund.setRefundFailReason(failReason);
            }
            refundItemManager.updateRefundItem(refundItem);
            if (!Boolean.valueOf(result)) {
                return false;
            }
        }
        return true;
    }

    //退款后更改es_order_item中的refund_price
    private void changeOrderRefundPrice(RefundDO refund, Double refundPrice) {
        String refundSn = refund.getSn();
        List<RefundGoodsDO> refundGoodsDOList = this.getRefundGoods(refundSn);
        double allowRefundPrice;
        String orderSn = refund.getOrderSn();
        for (RefundGoodsDO refundGoodsDO : refundGoodsDOList) {
            //DB中将可退款金额减去单个商品退补的金额
            Integer goodsId = refundGoodsDO.getGoodsId();
            OrderItemsDO orderItemsDO = orderQueryManager.queryOrderItem(orderSn, goodsId);
            if (ObjectUtils.isEmpty(orderItemsDO)) {
                throw new ServiceException(AftersaleErrorCode.E612.name(), AftersaleErrorCode.E612.describe());
            }
            allowRefundPrice = orderItemsDO.getRefundPrice();
            if (allowRefundPrice > 0 && refundPrice > 0) {
                int compare = BigDecimal.valueOf(allowRefundPrice).compareTo(BigDecimal.valueOf(refundPrice));
                if (compare >= 0) {
                    //此商品的可退款金额大于等于本次退款金额，商品的可退款金额修改为 可退款金额 减去 本次退款金额，本次退款金额清零
                    allowRefundPrice = CurrencyUtil.sub(allowRefundPrice, refundPrice);
                    refundPrice = 0.0d;
                } else {
                    //此商品的可退款金额小于本次退款金额，本次退款金额修改为 本次退款金额 减去 可退款金额，商品的可退款金额清零
                    refundPrice = CurrencyUtil.sub(refundPrice, allowRefundPrice);
                    allowRefundPrice = 0.0d;
                }
                this.daoSupport.execute("update es_order_items set refund_price=? where order_sn=? and goods_id=? ",
                        allowRefundPrice, orderSn, goodsId);
            }
        }
    }

    //获取订单可退金额和订单退款后剩余金额，调用更新退款后剩余收益方法
    private void updateRefundSurplusProfit(RefundDO refund){
        String orderSn = refund.getOrderSn();
        double newOrderPrice = orderQueryManager.getOrderRefundPrice(orderSn);
        double oldOrderPrice = CurrencyUtil.add(newOrderPrice, refund.getRefundPrice());
        OrderDetailDTO orderDetailDTO = orderQueryManager.getModel(refund.getOrderSn());
        // 审核维权订单时，待成团的订单不会重算佣金
        if (OrderTypeEnum.pintuan.name().equals(orderDetailDTO.getOrderType())) {
            if (!OrderStatusEnum.PAID_OFF.value().equals(orderDetailDTO.getOrderStatus())) {
                OrderDO orderDO = new OrderDO();
                BeanUtil.copyProperties(orderDetailDTO, orderDO);
                //更新收益
                distributionManager.updateRefundSurplusProfit(orderDO, oldOrderPrice, newOrderPrice);
            }
        } else {
            OrderDO orderDO = new OrderDO();
            BeanUtil.copyProperties(orderDetailDTO, orderDO);
            //更新收益
            distributionManager.updateRefundSurplusProfit(orderDO, oldOrderPrice, newOrderPrice);
        }
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void sellerStockIn (String sn, String remark, Permission permission) {

        RefundDO refund = this.daoSupport.queryForObject("select * from es_refund where sn =?", RefundDO.class, sn);

        // 验证退款单是否存在
        if (ObjectUtils.isEmpty(refund)) {
            throw new ServiceException(AftersaleErrorCode.E603.name(), "退款单不存在");
        }
        // 操作权限不是管理员就要验证是不是当前店铺的退款
        if (!Permission.ADMIN.equals(permission)) {
            Seller seller = UserContext.getSeller();
            if (!seller.getSellerId().equals(refund.getSellerId())) {
                throw new ServiceException(AftersaleErrorCode.E603.name(), "退款单不存在");
            }
        }
        // 校验退款单状态是否允许操作入库
        this.checkAllowable(refund, RefundOperateEnum.STOCK_IN);

        // 发送入库的消息 --》修改库存
        RefundChangeMsg refundStatusChangeMessage = new RefundChangeMsg(refund, RefundStatusEnum.STOCK_IN);
        amqpTemplate.convertAndSend(AmqpExchange.REFUND_STATUS_CHANGE, AmqpExchange.REFUND_STATUS_CHANGE + "_QUEUE",
                refundStatusChangeMessage);

        // 如果支持原路退回则审核通过后直接原路退款
        if (refund.getRefundWay().equals(RefundWayEnum.ORIGINAL.value()) && refund.getRefundPrice() > 0) {

            //获取到退款单详情，不同支付方式退款方式不用，循环调用退款返回
            String refundStatus;
            long dateline = DateUtil.getDateline();
            boolean isSuccess = this.batchOriginRefund(refund, dateline);

            if (isSuccess) {
                refundStatus = RefundStatusEnum.REFUNDING.value();
                this.daoSupport.execute("update es_refund set refund_status=?, refund_time=?, warehouse_remark=? where sn =?",
                        refundStatus, dateline, remark == null ? "" : remark, sn);

                //获取订单可退金额和订单退款后剩余金额，调用更新退款后剩余收益方法
                this.updateRefundSurplusProfit(refund);

                //发送原路退回成功消息
                refundStatusChangeMessage = new RefundChangeMsg(refund, RefundStatusEnum.REFUNDING);
                amqpTemplate.convertAndSend(AmqpExchange.REFUND_STATUS_CHANGE, AmqpExchange.REFUND_STATUS_CHANGE + "_QUEUE",
                        refundStatusChangeMessage);

            } else {
                // 退款失败，修改状态和失败原因
                refundStatus = RefundStatusEnum.REFUNDFAIL.value();
                this.daoSupport.execute("update es_refund set refund_status=?, refund_fail_reason=?, warehouse_remark=? where sn =?",
                        refundStatus, refund.getRefundFailReason(), remark == null ? "" : remark, sn);
                this.daoSupport.execute(
                        "update es_refund_item set item_status=?, refund_fail_reason=? where refund_sn =?",
                        RefundItemStatusEnum.FAIL.value(), refund.getRefundFailReason(), sn);
            }
        }
    }

    /**
     * 平台审核退款
     * @param refundApproval 退款VO
     * @return 退款VO
     */
    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public FinanceRefundApprovalVO approval(FinanceRefundApprovalVO refundApproval) {

        RefundDO refund = this.daoSupport.queryForObject("select * from es_refund where sn =?", RefundDO.class,
                refundApproval.getSn());
        if (refund == null) {
            throw new ServiceException(AftersaleErrorCode.E603.name(), "退款单不存在");
        }

        // RefundDetailDTO detail = this.getDetail(refundApproval.getSn());
        //权限校验
        this.checkAllowable(refund, RefundOperateEnum.ADMIN_REFUND);
        //金额校验
        Double agreeRefundPrice = refundApproval.getRefundPrice();
        this.checkMoney(refund, agreeRefundPrice);

        //假如同意的退款金额和申请时的退款金额不同，先删除所有旧的退款单明细，后重算退款详清单中的退款金额
        this.recountRefundItem(refund, agreeRefundPrice);

        //获取到退款单详情，不同支付方式退款方式不用，循环调用退款返回
        long dateline = DateUtil.getDateline();
        boolean success = this.batchOriginRefund(refund, dateline);
        if (success) {
            this.daoSupport.execute("update es_refund set refund_price=?, refund_status=?, finance_remark=?, refund_time=? where sn=?",
                    refund.getRefundPrice(), RefundStatusEnum.COMPLETED.value(), refundApproval.getRemark() == null ? "" : refundApproval.getRemark(),
                    dateline, refundApproval.getSn());

            //获取订单可退金额和订单退款后剩余金额，调用更新退款后剩余收益方法
            this.updateRefundSurplusProfit(refund);

            // 发送管理员审核的消息
            RefundChangeMsg refundStatusChangeMessage = new RefundChangeMsg(refund,
                    RefundStatusEnum.REFUNDING);
            amqpTemplate.convertAndSend(AmqpExchange.REFUND_STATUS_CHANGE, AmqpExchange.REFUND_STATUS_CHANGE + "_QUEUE",
                    refundStatusChangeMessage);
        } else {
            this.daoSupport.execute("update es_refund set refund_price=? ,refund_status=?, finance_remark=? where sn=?",
                    refund.getRefundPrice(), RefundStatusEnum.REFUNDFAIL.value(), refundApproval.getRemark() == null ? "" : refundApproval.getRemark(),
                    refundApproval.getSn());
        }
        return refundApproval;
    }
    /**
     * ***************************************重生成退款详清单的方法**********************************************
     * 假如同意的退款金额和申请时的退款金额不同,重算退款详清单中的退款金额
     */
    private void recountRefundItem(RefundDO refund, Double agreeRefundPrice){
        //获取以前申请的退款金额和审核通过的退款金额进行比较
        Double oldRefundPrice = refund.getRefundPrice();
        refund.setRefundPrice(agreeRefundPrice);
        //假如同意的退款金额和申请时的退款金额不同，先删除所有旧的退款单明细，后重算退款详清单中的退款金额
        if (BigDecimal.valueOf(agreeRefundPrice).compareTo(BigDecimal.valueOf(oldRefundPrice)) != 0) {
            //假如之前生成的退款单明细有微信退款的，要把退款单明细状态改为disabled失效
            refundItemManager.updateRefundItemDisabled(refund.getSn());
            OrderDetailDTO orderDetail = this.orderQueryManager.getModel(refund.getOrderSn());
            PaymentPluginManager plugin = this.findPlugin(orderDetail.getPaymentPluginId());
            if (ObjectUtils.isEmpty(plugin)) {
                throw new ServiceException(AftersaleErrorCode.E610.name(), "退款单创建失败");
            }
            plugin.createRefundItem(refund, orderDetail);
        }
    }


    @Override
    public Page<RefundDTO> query(RefundQueryParamVO param) {
        StringBuilder sql = new StringBuilder();

        List<Object> termList = new ArrayList<>();
        List<String> sqlSplit = new ArrayList<>();

        sql.append("select * from es_refund ");

        String sn = param.getSn();
        if (StringUtil.notEmpty(sn)) {
            sqlSplit.add(" sn=? ");
            termList.add(sn);
        }

        String refundStatus = param.getRefundStatus();
        if (StringUtil.notEmpty(refundStatus)) {
            sqlSplit.add(" refund_status=? ");
            termList.add(refundStatus);
        }

        List<String> refundStatusArray = param.getRefundStatusArray();
        if (CollectionUtils.isNotEmpty(refundStatusArray)) {
            String str = SqlUtil.getInSql(refundStatusArray.toArray(), termList);
            sqlSplit.add(" refund_status in ("+str+") ");
        }

        Integer sellerId = param.getSellerId();
        if (sellerId != null && sellerId != 0) {
            sqlSplit.add(" seller_id=? ");
            termList.add(sellerId);
        }

        String sellerName = param.getSellerName();
        if (StringUtil.notEmpty(sellerName)) {
            sqlSplit.add(" seller_name like ? ");
            termList.add("%" + sellerName + "%");
        }

        Integer memberId = param.getMemberId();
        if (memberId != null) {
            sqlSplit.add(" member_id=? ");
            termList.add(memberId);
        }

        String orderSn = param.getOrderSn();
        if (StringUtil.notEmpty(orderSn)) {
            sqlSplit.add(" order_sn=? ");
            termList.add(orderSn);
        }

        String refuseType = param.getRefuseType();
        if (StringUtil.notEmpty(refuseType)) {
            sqlSplit.add(" refuse_type=? ");
            termList.add(refuseType);
        }

        String refundType = param.getRefundType();
        if (StringUtil.notEmpty(refundType)) {
            sqlSplit.add(" refund_type=? ");
            termList.add(refundType);
        }

        // 按时间查询
        String startTime = param.getStartTime();
        String endTime = param.getEndTime();
        if (StringUtil.notEmpty(startTime)) {
            sqlSplit.add(" create_time >= ? ");
            termList.add(startTime);
        }

        if (StringUtil.notEmpty(endTime)) {
            sqlSplit.add(" create_time <= ? ");
            termList.add(endTime);

        }

        //退款方式
        if (StringUtil.notEmpty(param.getRefundWay())) {
            sqlSplit.add(" refund_way = ? ");
            termList.add(param.getRefundWay());
        }

        //对sql条件语句进行拼接
        String sqlSplicing = SqlSplicingUtil.sqlSplicing(sqlSplit);
        if (!StringUtil.isEmpty(sqlSplicing)) {
            sql.append(sqlSplicing);
        }

        sql.append(" order by create_time desc");
        Page page = this.daoSupport.queryForPage(sql.toString(), param.getPageNo(), param.getPageSize(),
                RefundDTO.class, termList.toArray());

        return page;
    }

    @Override
    public RefundDetailDTO getDetail(String sn) {
        // 退款单
        RefundDTO refundDTO = this.daoSupport.queryForObject("select * from es_refund where sn =?", RefundDTO.class, sn);

        if (refundDTO == null) {
            throw new ServiceException(AftersaleErrorCode.E603.name(), "退款单不存在");
        }

        // 退款订单状态
        Map orderStatusMap = this.daoSupport.queryForMap("select order_status from es_order where sn = ?", refundDTO.getOrderSn());
        String orderStatus = (String) orderStatusMap.get("order_status");
        // 订单已收货和订单已完成
        int isShipped = orderStatus.equals(OrderStatusEnum.ROG.name()) || orderStatus.equals(OrderStatusEnum.COMPLETE.name()) ? 1 : 0;
        refundDTO.setIsShipped(isShipped);
        refundDTO.setOrderStatus(orderStatus);

        // 退款商品信息
        List<RefundGoodsDO> refundGoodsDOS = this.daoSupport.queryForList("select * from es_refund_goods where refund_sn=? ", RefundGoodsDO.class, sn);
        RefundDetailDTO refundDetail = new RefundDetailDTO();
        refundDetail.setRefund(refundDTO);
        refundDetail.setRefundGoods(refundGoodsDOS);
        return refundDetail;
    }

    @Override
    public RefundDetailDTO getDetailByOrderSn(String orderSn) {
        // 退款单
        RefundDTO refundDTO = this.daoSupport.queryForObject("select * from es_refund where order_sn =?", RefundDTO.class, orderSn);

        if (refundDTO == null) {
            throw new ServiceException(AftersaleErrorCode.E603.name(), "退款单不存在");
        }

        // 退款订单状态
        Map orderStatusMap = this.daoSupport.queryForMap("select order_status from es_order where sn = ?", refundDTO.getOrderSn());
        String orderStatus = (String) orderStatusMap.get("order_status");
        // 订单已收货和订单已完成
        int isShipped = orderStatus.equals(OrderStatusEnum.ROG.name()) || orderStatus.equals(OrderStatusEnum.COMPLETE.name()) ? 1 : 0;
        refundDTO.setIsShipped(isShipped);
        refundDTO.setOrderStatus(orderStatus);

        // 退款商品信息
        List<RefundGoodsDO> refundGoodsDOS = this.daoSupport.queryForList("select * from es_refund_goods where refund_sn=? ", RefundGoodsDO.class, refundDTO.getSn());
        List<RefundGoodsDTO> refundGoodsDTOS =new ArrayList<>();
        for (RefundGoodsDO refundGoodsDO : refundGoodsDOS) {
            RefundGoodsDTO refundGoodsDTO = new RefundGoodsDTO();
            BeanUtil.copyProperties(refundGoodsDO,refundGoodsDTO);
            List<SpecValuesDO> specValuesDOS = JSON.parseArray(refundGoodsDO.getSpecJson(), SpecValuesDO.class);
            refundGoodsDTO.setGoodsSpec(specValuesDOS);
            refundGoodsDTOS.add(refundGoodsDTO);
        }
        RefundDetailDTO refundDetail = new RefundDetailDTO();
        refundDetail.setRefund(refundDTO);
        refundDetail.setRefundGoodsDTO(refundGoodsDTOS);
        return refundDetail;
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public RefundDO cancelRefund(String sn) {
        RefundDO refund = this.daoSupport.queryForObject("select * from es_refund where sn =?", RefundDO.class, sn);
        Buyer buyer = UserContext.getBuyer();
        if (refund == null || !buyer.getUid().equals(refund.getMemberId())) {
            throw new ServiceException(AftersaleErrorCode.E603.name(), "退款单不存在");
        }

        this.checkAllowable(refund, RefundOperateEnum.CANCEL);

        this.daoSupport.execute("update es_refund set refund_status=? where sn =?", RefundStatusEnum.CANCEL.value(), sn);

        return refund;
    }

    @Override
    public List<RefundGoodsDO> getRefundGoods(String sn) {
        return this.daoSupport.queryForList("select * from es_refund_goods where refund_sn=? ", RefundGoodsDO.class, sn);
    }

    @Override
    public List<RefundDO> queryNoReturnOrder() {
        String sql = "select *,sn refund_sn from es_refund where refund_status = ? and refund_way = 'ORIGINAL'";
        return this.daoSupport.queryForList(sql, RefundDO.class, RefundStatusEnum.REFUNDING.value());
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void update(List<RefundDO> list) {
        if (StringUtil.isNotEmpty(list)) {
            for (RefundDO refund : list) {
                Map map = new HashedMap(2);
                map.put("refund_status", refund.getRefundStatus());
                Map where = new HashMap(2);
                where.put("sn", refund.getSn());
                this.daoSupport.update("es_refund", map, where);
            }
        }
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void sellerRefund(String sn, String remark) {

        Seller seller = UserContext.getSeller();
        RefundDO refund = this.daoSupport.queryForObject("select * from es_refund where sn =?", RefundDO.class, sn);
        // 验证退款单是否存在及是否当前卖家的退款单
        if (refund == null || !seller.getSellerId().equals(refund.getSellerId())) {
            throw new ServiceException(AftersaleErrorCode.E603.name(), "退款单不存在");
        }

        this.checkAllowable(refund, RefundOperateEnum.SELLER_REFUND);

        this.daoSupport.execute("update es_refund set refund_status=? ,finance_remark = ?,refund_time= ? where sn=?",
                RefundStatusEnum.COMPLETED.value(), remark == null ? "" : remark, DateUtil.getDateline(), sn);
        // 发送管理员审核的消息
        RefundChangeMsg refundStatusChangeMessage = new RefundChangeMsg(refund,
                RefundStatusEnum.REFUNDING);
        amqpTemplate.convertAndSend(AmqpExchange.REFUND_STATUS_CHANGE, "systemRefund-apply-ROUTING_KEY",
                refundStatusChangeMessage);
    }

    @Override
    public List<ExportRefundExcelVO> exportExcel(long startTime, long endTime) {
        String sql = "select * from es_refund where create_time >= ? and create_time <= ? ";
        return this.daoSupport.queryForList(sql, ExportRefundExcelVO.class, startTime, endTime);
    }

    @Override
    public RefundApplyVO refundApply(String orderSn, Integer skuId) {
        RefundApplyVO refundApplyVO = new RefundApplyVO();
        OrderDetailVO order = orderQueryManager.getModel(orderSn, null);

        if (!order.getMemberId().equals(UserContext.getBuyer().getUid())) {
            throw new ServiceException(AftersaleErrorCode.E604.name(), "订单不存在");
        }
        // 策略模式加载对应支付处理器
        PaymentMethodDO paymentMethodDO = paymentMethodManager.getByPluginId(order.getPaymentPluginId());
        //判断是否支持原路退回
        if (paymentMethodDO != null && paymentMethodDO.getIsRetrace() == 1) {
            refundApplyVO.setOriginalWay("yes");
        } else {
            refundApplyVO.setOriginalWay("no");
        }
        refundApplyVO.setReturnPoint(0);
        //如果不传skuid则为整单申请 退款金额为订单支付金额
        if (skuId == null) {
            //退款申请不退运费(查询出order_item中订单对应各项商品的集合，累加它的可退款金，这个可退款金额中本身就不包括运费)
            double allowOrderRefundPrice = orderQueryManager.getOrderRefundPrice(orderSn);
            refundApplyVO.setReturnMoney(allowOrderRefundPrice);
            refundApplyVO.setOrder(order);

            List<RefundSkuVO> refundSkuVOS = new ArrayList<>();
            for (OrderSkuVO orderSkuVo : order.getOrderSkuList()) {
                refundSkuVOS.add(
                        initRefundSkuVO(orderSkuVo, order));
            }
            refundApplyVO.setSkuList(refundSkuVOS);
        } else {
            List<RefundSkuVO> list = new ArrayList<>();
            for (OrderSkuVO orderSkuVO : order.getOrderSkuList()) {
                if (orderSkuVO.getSkuId().equals(skuId)) {
                    list.add(initRefundSkuVO(orderSkuVO, order));

                    //退款金额
                    double returnMoney;
                    if (orderSkuVO.getNum() >= 2) {
                        int num = orderSkuVO.getNum() - 1;
                        returnMoney = CurrencyUtil.add(CurrencyUtil.mul(list.get(0).getRefundPrice(), num), list.get(0).getLastRefundPrice());
                    } else {
                        returnMoney = CurrencyUtil.mul(list.get(0).getRefundPrice(), orderSkuVO.getNum());
                    }
                    refundApplyVO.setReturnMoney(returnMoney);
                }
            }
            refundApplyVO.setOrder(order);
            refundApplyVO.setSkuList(list);
        }
        return refundApplyVO;
    }

    @Override
    public Integer getAfterSaleCount(Integer memberId, Integer sellerId) {
        StringBuilder sql = new StringBuilder("select count(*) from es_refund where refund_status in( ? , ?)  ");
        List<Object> term = new ArrayList<>();
        term.add(RefundStatusEnum.APPLY.value());
        term.add(RefundStatusEnum.STOCK_IN.value());
        if (memberId != null) {
            sql.append("and member_id = ? ");
            term.add(memberId);
        }

        if (sellerId != null) {
            sql.append("and seller_id = ? ");
            term.add(sellerId);
        }
        return this.daoSupport.queryForInt(sql.toString(), term.toArray());
    }

    @Override
    public List<RefundDO> queryRefundStatus() {
        List<RefundDO> refundDOS = this.queryNoReturnOrder();
        for (RefundDO refundDO : refundDOS) {
            String status = refundDO.getRefundStatus();
            //微信支付和组合支付时，查询微信支付结果
            if (AccountTypeEnum.WEIXINPAY.value().equals(refundDO.getAccountType()) || AccountTypeEnum.MERGEPAY.value().equals(refundDO.getAccountType())) {
                RefundItemDO refundItem = refundItemManager.queryRefundItemByRefundIdAndPaymentPluginId(refundDO.getId(), PaymentPluginEnum.weixinPayPlugin.name());
                if (!ObjectUtils.isEmpty(refundItem)) {
                    status = refundManager.queryRefundStatus(refundItem.getPayOrderNo(), refundItem.getRefundSn());
                }

            } else if (AccountTypeEnum.WALLETPAY.value().equals(refundDO.getAccountType())) {
                //钱包支付时，判断退款单明细中退款的状态
                RefundItemDO refundItem = refundItemManager.queryRefundItemByRefundIdAndPaymentPluginId(refundDO.getId(), PaymentPluginEnum.walletPayPlugin.name());
                if (!ObjectUtils.isEmpty(refundItem)) {
                    if (RefundItemStatusEnum.SUCCESS.value().equals(refundItem.getItemStatus())) {
                        status = RefundStatusEnum.COMPLETED.value();
                    } else {
                        status = RefundStatusEnum.REFUNDFAIL.value();
                    }
                }
            } else {
                //其他支付方式，暂时不考虑，退款状态不变
                continue;
            }

            refundDO.setRefundStatus(status);
        }
        this.update(refundDOS);
        return refundDOS;
    }

    @Override
    public void editRefundShopName(Integer shopId, String shopName) {
        String sql = "update es_refund set seller_name = ? where seller_id = ? ";
        this.daoSupport.execute(sql, shopName, shopId);
    }

    @Override
    public Page<RefundOrderDTO> queryRefundOrders(RefundQueryParamVO queryParam) {
        String sql = "SELECT  ord.*, " +
                "ref.id refund_id, ref.sn refund_sn, ref.refund_status , ref.refund_price, ref.refund_reason, ref.refund_type, ref.refuse_type, ref.create_time " +
                "FROM  es_refund ref  INNER JOIN es_order ord " +
                "ON ref.order_sn = ord.sn   AND ref.refund_status = 'APPLY'   AND ref.seller_id =(?)";

        Page refundOrderPage = this.daoSupport.queryForPage(sql, queryParam.getPageNo(), queryParam.getPageSize(),
                RefundOrderDTO.class, queryParam.getSellerId());

        return refundOrderPage;
    }

    /**
     * 获取订单项信息
     *
     * @param orderSn 订单编号
     * @param skuId   商品SkuId
     * @return 订单详情
     */
    private OrderItemsDO getOrderItems(String orderSn, Integer skuId) {
        String sql = "select * from es_order_items where order_sn = ? and product_id = ?";
        return this.daoSupport.queryForObject(sql, OrderItemsDO.class, orderSn, skuId);
    }

    /**
     * 售后skuvo 初始化属性
     *
     * @param skuVO 订单商品项
     * @param order 订单明细
     * @return RefundSkuVO
     */
    private RefundSkuVO initRefundSkuVO(OrderSkuVO skuVO, OrderDetailVO order) {
        RefundSkuVO refundSkuVO = new RefundSkuVO(skuVO);

        //获取订单项数据
        OrderItemsDO itemsDO = this.getOrderItems(order.getSn(), skuVO.getSkuId());

        //判断订单项的可退款金额是否为空或者为0
        if (itemsDO.getRefundPrice() == null || itemsDO.getRefundPrice() == 0) {
            refundSkuVO.setRefundPrice(0.00);
            refundSkuVO.setLastRefundPrice(0.00);
        } else {
            double refundPrice = itemsDO.getRefundPrice();
            refundSkuVO.setRefundPrice(CurrencyUtil.div(refundPrice, skuVO.getNum(), 4));

            if (skuVO.getNum() >= 2) {
                int num = skuVO.getNum() - 1;
                refundSkuVO.setLastRefundPrice(CurrencyUtil.sub(refundPrice, CurrencyUtil.mul(refundSkuVO.getRefundPrice(), num)));
            }
        }

        return refundSkuVO;
    }

    /**
     * 卖家退款申请
     *
     * @param buyerRefundApply 退款申请VO
     * @return 退款单
     */
    private RefundDO buyerRefund(BuyerRefundApplyVO buyerRefundApply) {
        Buyer buyer = UserContext.getBuyer();

        //获取订单信息，判断订单有效性
        OrderDetailDTO order = orderQueryManager.getModel(buyerRefundApply.getOrderSn());

        //不存在的订单或者不属于当前会员的订单进行校验
        if (order == null || !buyer.getUid().equals(order.getMemberId())) {
            throw new ServiceException(AftersaleErrorCode.E604.name(), "订单不存在");
        }

        if(order.getServiceStatus().equals(ServiceStatusEnum.APPLY.value())){
            throw new ServiceException(AftersaleErrorCode.E604.name(), "订单已申请售后");
        }
        //此订单已支付，已发货为部分发货的订单，不能申请退款
        if (order.getOrderStatus().equals(OrderStatusEnum.PAID_OFF.value()) && order.getShipStatus().equals(ShipStatusEnum.SHIP_YES.value())) {
            throw new ServiceException(AftersaleErrorCode.E613.name(), AftersaleErrorCode.E613.describe());
        }

        return innerRefund(buyerRefundApply, order);
    }

    /**
     * 具体操作退款
     *
     * @param buyerRefundApply 买家退款申请VO
     * @param order 订单详细信息DTO
     * @return 退款单
     */
    private RefundDO innerRefund(BuyerRefundApplyVO buyerRefundApply, OrderDetailDTO order) {

        //实际退款金额
        Double refundPrice = 0.0d;

        //获取订单可退款金额
        double allowRefundPrice = this.orderQueryManager.getOrderRefundPrice(order.getSn());

        //如果订单的可退款金额小于等于0，那么不允许退款
        if (allowRefundPrice <= 0) {
            throw new ServiceException(AftersaleErrorCode.E609.name(), AftersaleErrorCode.E609.describe());
        }

        //获取订单SKU信息
        List<OrderSkuDTO> orderSkuList = order.getOrderSkuList();
        OrderSkuDTO orderSkuDTO = null;

        //根据当前时间生成退款单号
        String refundSn = DateUtil.toString(DateUtil.getDateline(), "yyMMddHHmmss") + (new Random().nextInt(999) + 100);

        //skuId为空，证明是付款后取消订单；不为空证明是确认收货后申请售后
        if (buyerRefundApply.getSkuId() == null) {

            //实际退款金额等于订单的可退款总额
            refundPrice = allowRefundPrice;

            for (OrderSkuDTO orderSku : orderSkuList) {
                //生成退货商品表
                this.refundGoods(orderSku, orderSku.getNum(), refundSn);
            }

            //修改订单的售后状态
            orderOperateManager.updateOrderServiceStatus(order.getSn(), ServiceStatusEnum.APPLY.name());

        } else {

            for (OrderSkuDTO orderSku : orderSkuList) {

                //判断要申请售后的SKU是不是存在于订单SKU信息中
                if (orderSku.getSkuId() == (buyerRefundApply.getSkuId()).intValue()) {
                    //获取订单项数据
                    OrderItemsDO orderItem = this.getOrderItems(order.getSn(), orderSku.getSkuId());

                    //如果可退款金额为0(针对单个商品申请退款退货的情况)
                    if (orderItem == null || orderItem.getRefundPrice() == null || orderItem.getRefundPrice() == 0) {
                        throw new ServiceException(AftersaleErrorCode.E609.name(), AftersaleErrorCode.E609.describe());
                    }
                    // ordersku.service_status= NOT_APPLY
                    if (!orderSku.getServiceStatus().equals(ServiceStatusEnum.NOT_APPLY.name())) {
                        throw new ServiceException(AftersaleErrorCode.E601.name(), "操作不允许");
                    }

                    orderSkuDTO = orderSku;

                    //退款时价格为货品的总价格，退货时价格为单个货品的价格乘以数量
                    if (buyerRefundApply.getRefuseType().equals(RefuseTypeEnum.RETURN_MONEY.value())) {
                        buyerRefundApply.setReturnNum(orderSku.getNum());
                        refundPrice = orderItem.getRefundPrice();
                    } else {
                        //如果没有传递退货数量，则默认为购买数量
                        if (buyerRefundApply.getReturnNum() == null) {
                            buyerRefundApply.setReturnNum(orderSku.getNum());
                        }

                        if (orderSku.getNum() < buyerRefundApply.getReturnNum()) {
                            throw new ServiceException(AftersaleErrorCode.E607.name(), "申请售后货品数量不能大于购买数量");
                        }

                        //如果申请退货的数量等于订单购买的数量
                        if (orderSku.getNum().equals(buyerRefundApply.getReturnNum())) {
                            refundPrice = orderItem.getRefundPrice();
                        } else {
                            refundPrice = CurrencyUtil.mul(buyerRefundApply.getReturnNum(), CurrencyUtil.div(orderItem.getRefundPrice(), orderItem.getNum(), 4));
                        }
                    }
                    //生成退货商品表
                    this.refundGoods(orderSkuDTO, buyerRefundApply.getReturnNum(), refundSn);
                    //修改订单项的售后状态为已申请
                    List<OrderSkuDTO> orderSkuDTOList = new ArrayList<>();
                    orderSku.setServiceStatus(ServiceStatusEnum.APPLY.name());
                    orderSkuDTOList.add(orderSku);
                    orderOperateManager.updateOrderItemServiceStatus(order.getSn(), orderSkuDTOList);
                }
            }
            //判断商品是否存在
            if (orderSkuDTO == null) {
                throw new ServiceException(AftersaleErrorCode.E602.name(), "商品不存在");
            }
        }


        //退款单入库
        RefundDO refund = new RefundDO();
        refund.setSn(refundSn);
        refund.setOrderSn(order.getSn());
        refund.setCustomerRemark(buyerRefundApply.getCustomerRemark());
        refund.setRefundPrice(refundPrice);
        refund.setRefundReason(buyerRefundApply.getRefundReason());
        refund.setRefundType(RefundTypeEnum.AFTER_SALE.value());
        refund.setPaymentType(order.getPaymentType());
        refund.setPayOrderNo(order.getPayOrderNo());

        //判断当前支付方式是否支持原路退回,如果不支持则退款方式不能为空
        PaymentMethodDO paymentMethodDO = paymentMethodManager.getByPluginId(order.getPaymentPluginId());
        String refundWay = RefundWayEnum.ORIGINAL.name();
        if (paymentMethodDO == null || paymentMethodDO.getIsRetrace() == 0) {
            refundWay = RefundWayEnum.OFFLINE.name();
            if (buyerRefundApply.getAccountType() == null) {
                throw new ServiceException(AftersaleErrorCode.E605.name(), "退款方式必填");
            }

            //银行转账
            if (AccountTypeEnum.BANKTRANSFER.value().equals(buyerRefundApply.getAccountType())) {
                refund.setBankAccountName(buyerRefundApply.getBankAccountName());
                refund.setBankAccountNumber(buyerRefundApply.getBankAccountNumber());
                refund.setBankDepositName(buyerRefundApply.getBankDepositName());
                refund.setBankName(buyerRefundApply.getBankName());
            } else {
                //支付宝或者微信
                refund.setReturnAccount(buyerRefundApply.getReturnAccount());
            }
            refund.setAccountType(buyerRefundApply.getAccountType());
        } else {
            //退款单确定退款账户类型
            this.getAccountType(refund, paymentMethodDO);
        }
        refund.setMemberId(order.getMemberId());
        refund.setMemberName(StringUtil.isEmpty(order.getShipName())?order.getShipName():order.getMemberName());
        refund.setSellerId(order.getSellerId());
        refund.setSellerName(order.getSellerName());
        refund.setTradeSn(order.getTradeSn());
        refund.setOrderCreateTime(order.getCreateTime());
        refund.setPayOrderNo(order.getPayOrderNo());
        refund.setCreateTime(DateUtil.getDateline());
        refund.setRefundStatus(RefundStatusEnum.APPLY.value());
        refund.setRefuseType(buyerRefundApply.getRefuseType());
        refund.setRefundWay(refundWay);
        refund.setRefundGift(JsonUtil.objectToJson(order.getGiftList()));

        this.daoSupport.insert("es_refund", refund);
        refund.setId(this.daoSupport.getLastId("es_refund"));
        buyerRefundApply.setRefundSn(refundSn);

        //根据支付插件确定退款方式和支付时的流水号,生成refund_item，用于向第三方发起退款
        if (!ObjectUtils.isEmpty(paymentMethodDO)) {
            PaymentPluginManager plugin = this.findPlugin(paymentMethodDO.getPluginId());
            if (plugin != null) {
                plugin.createRefundItem(refund, order);
            }
        }

        // 发送申请退款的消息
        RefundChangeMsg refundStatusChangeMessage = new RefundChangeMsg(refund, RefundStatusEnum.APPLY);
        amqpTemplate.convertAndSend(AmqpExchange.REFUND_STATUS_CHANGE, AmqpExchange.REFUND_STATUS_CHANGE + "_QUEUE",
                refundStatusChangeMessage);

        return refund;
    }

    /**
     * 进行操作校验 看此状态下是否允许此操作
     *
     * @param refund        退款VO
     * @param refundOperate 进行的操作
     */
    private void checkAllowable(RefundDO refund, RefundOperateEnum refundOperate) {

        // 退款当前流程状态
        String status = refund.getRefundStatus();
        RefundStatusEnum refundStatus = RefundStatusEnum.valueOf(status);

        // 退货/退款
        String refuseType = refund.getRefuseType();
        RefuseTypeEnum type = RefuseTypeEnum.valueOf(refuseType);

        // 货到付款/在线支付
        String paymentType = refund.getPaymentType();
        PaymentTypeEnum payment = PaymentTypeEnum.valueOf(paymentType);

        boolean allowble = RefundOperateChecker.checkAllowable(type, payment, refundStatus, refundOperate);
        if (!allowble) {
            throw new ServiceException(AftersaleErrorCode.E601.name(), "操作不允许");
        }
    }

    /**
     * 记录操作日志
     *
     * @param sn 退款单ID
     * @param operator 操作者
     * @param detail 日志详细
     */
    private void log(String sn, String operator, String detail) {
        RefundLogDO refundLog = new RefundLogDO();
        refundLog.setOperator(operator);
        refundLog.setRefundSn(sn);
        refundLog.setLogtime(DateUtil.getDateline());
        refundLog.setLogdetail(detail);

        this.daoSupport.insert("es_refund_log", refundLog);

    }

    /**
     * 退货商品入库
     *
     * @param orderSkuDTO 订单货品项DTO
     * @param num 退货数量
     * @param refundSn 退款单号
     */
    private void refundGoods(OrderSkuDTO orderSkuDTO, Integer num, String refundSn) {
        // 像退货商品表插入数据
        RefundGoodsDO refundGoods = new RefundGoodsDO(orderSkuDTO);
        refundGoods.setReturnNum(num);
        // 实际支付的商品单价
        refundGoods.setPrice(orderSkuDTO.getPurchasePrice());
        refundGoods.setRefundSn(refundSn);
        this.daoSupport.insert("es_refund_goods", refundGoods);
    }

    /**
     * 校验退款金额
     *
     * @param refund 退款单
     * @param price 审核通过的退款金额
     */
    private void checkMoney(RefundDO refund, Double price) {

        //获取退款单信息
        RefundDetailDTO detail = this.getDetail(refund.getSn());
        List<RefundGoodsDO> refundGoods = detail.getRefundGoods();
        double allowRefundPrice = 0.0d;
        String orderSn = refund.getOrderSn();
        if (refund.getRefundType().equals(RefundTypeEnum.CANCEL_ORDER.value())) {
            //取消订单时，可退款金额为所有商品的可退款金额 + 运费
            allowRefundPrice = orderQueryManager.getOrderRefundPrice(orderSn);
            OrderDetailDTO order = orderQueryManager.getModel(orderSn);
            allowRefundPrice = CurrencyUtil.add(allowRefundPrice, order.getShippingPrice());
        } else {
            //申请售后时，可退款金额为所有申请售后的商品可退款金额之和
            for (RefundGoodsDO refundGood : refundGoods) {
                OrderItemsDO orderItemsDO = orderQueryManager.queryOrderItem(orderSn, refundGood.getGoodsId());
                allowRefundPrice = CurrencyUtil.add(allowRefundPrice, orderItemsDO.getRefundPrice());
            }
        }
        if (price != 0) {
            //审核时可退款金额规则
            if (price > allowRefundPrice || price < 0) {
                throw new ServiceException(AftersaleErrorCode.E600.name(), "退款金额不能大于可退款金额");
            }
        }
    }

    @Override
    public Integer getAfterSaleCount() {
        Integer sellerId = UserContext.getSeller().getSellerId();
        // 当天开始时间时间戳
        long startOfTodDay = DateUtil.startOfTodDay();
        // 当天结束时间时间戳
        long endOfTodDay = DateUtil.endOfTodDay();
        String sql =" SELECT count(1) as afterSaleCount from es_order o  where o.payment_time >= ?  and o.payment_time<= ? and o.seller_id = ? and o.pay_status = 'PAY_YES' ";
        return this.daoSupport.queryForInt(sql,startOfTodDay,endOfTodDay,sellerId);
    }

    @Override
    public List<String> spreadRefund(String[] orderSnList, SpreadRefundDTO spreadRefundDTO, Seller seller) {
        //参数校验
        Assert.isTrue(orderSnList != null && orderSnList.length > 0, "订单号不得为空");
        Assert.isTrue(spreadRefundDTO != null, "退补差价数据不得为空");
        String refundType = spreadRefundDTO.getRefundType();
        Assert.isTrue(StringUtils.hasText(refundType), "售后类型不得为空");
        String refundReason = spreadRefundDTO.getRefundReason();
        Assert.isTrue(StringUtils.hasText(refundReason), "退款原因不得为空");
        String refundDescription = spreadRefundDTO.getRefundDescription();
        Assert.isTrue(StringUtils.hasText(refundDescription), "退款描述不得为空");

        //获取上报异常的店员信息
        Clerk clerk = clerkManager.getClerkByMemberId(seller.getUid());
        Assert.notNull(clerk, "获取店员信息信息异常");

        //处理返还金额是随机数还是固定值
        Double fixRefund = spreadRefundDTO.getFixRefund();
        Double refundPrice;
        List<String> result = new ArrayList<>();

        for (String orderSn : orderSnList) {
            Assert.hasText(orderSn, "订单号不得为空");
            //校验随机金额和固定值并获取确切退补金额
            refundPrice = this.checkAndGetRefundPrice(fixRefund, spreadRefundDTO);
            try {
                //获取订单信息
                OrderDetailDTO orderDetailDTO = orderQueryManager.getModel(orderSn);
                //单个订单状态和订单服务状态校验（已取消，已申请，已失效无法退款）
                String orderStatus = orderDetailDTO.getOrderStatus();
                Assert.isTrue(StringUtils.hasText(orderStatus), "订单状态获取失败，请联系管理员");
                Assert.isTrue(!OrderStatusEnum.CANCELLED.value().equals(orderStatus), "订单已取消，无法申请退款");
                String serviceStatus = orderDetailDTO.getServiceStatus();
                Assert.isTrue(StringUtils.hasText(serviceStatus), "订单服务状态获取失败，请联系管理员");
                // Assert.isTrue(!ServiceStatusEnum.APPLY.value().equals(serviceStatus), "订单已申请售后");
                // Assert.isTrue(!ServiceStatusEnum.EXPIRED.value().equals(serviceStatus),"订单售后已失效");

                //校验订单有效性（是否属于当前店铺）、是否支付、以及校验退款金额
                this.checkOrderAvailability(orderDetailDTO, seller.getSellerId());

                //校验订单退款金额和可退款金额，多退少补不能退运费
                double allowRefundPrice = this.orderQueryManager.getOrderRefundPrice(orderDetailDTO.getSn());
                double payMoneyExcludeShippingPrice = CurrencyUtil.sub(orderDetailDTO.getPayMoney(), orderDetailDTO.getShippingPrice());
                this.checkRefundPrice(allowRefundPrice, refundPrice, payMoneyExcludeShippingPrice);

                RefundDO refund = new RefundDO();
                BeanUtil.copyProperties(orderDetailDTO, refund);
                //根据当前时间生成退款单号
                refund.setSn(DateUtil.toString(DateUtil.getDateline(), "yyMMddHHmmss") + (new Random().nextInt(999) + 100));
                refund.setOrderSn(orderDetailDTO.getSn());
                refund.setOrderCreateTime(orderDetailDTO.getCreateTime());
                //客户备注-->退款描述
                refund.setSellerRemark(refundDescription);
                refund.setRefundPrice(refundPrice);
                refund.setRefundReason(RefundReasonEnum.valueOf(refundReason).value());
                //售后类型(取消订单,申请售后,多退少补)
                refund.setRefundType(RefundTypeEnum.valueOf(refundType).value());
                //多退少补的退款方式为全部钱包退款
                refund.setAccountType(AccountTypeEnum.WALLETPAY.name());
                refund.setCreateTime(DateUtil.getDateline());
                //售后状态：申请中，完成，失败
                String refundStatus = RefundStatusEnum.APPLY.value();
                refund.setRefundStatus(refundStatus);
                //退款(货)类型(退款/退货)
                refund.setRefuseType(RefuseTypeEnum.RETURN_MONEY.value());
                //退款方式（原路退回）
                refund.setRefundWay(RefundWayEnum.ORIGINAL.name());
                //赠品信息
                refund.setRefundGift(JsonUtil.objectToJson(orderDetailDTO.getGiftList()));
                //审核人
                refund.setAuditName(clerk.getClerkName());
                this.daoSupport.insert("es_refund", refund);
                refund.setId(this.daoSupport.getLastId("es_refund"));

                //生成 1 条退款单明细记录---》钱包退款（实际是转账逻辑，转完扣减refund_item可退款金额）
                RefundItemDO refundItem = new RefundItemDO(refund);
                refundItem.setRefundPrice(refundPrice);
                refundItem.setPaymentPluginId(PaymentPluginEnum.walletPayPlugin.name());
                refundItemManager.insertRefundItem(refundItem);

                //初始化钱包转账请求DTO
                TransferReqDTO transferReqDTO = this.buildTransferReqDTO(refund, refundItem, clerk);
                //发起退款，返回操作结果和原因
                TransferRespDTO transferRespDTO = accountManager.transfer(transferReqDTO);

                refund.setRefundTime(DateUtil.getDateline());
                String refundFailReason = "";
                if (transferRespDTO.isSuccess()) {
                    //退款状态为完成
                    refundStatus = RefundStatusEnum.COMPLETED.value();
                    result.add("订单号：[" + orderSn + "]，退款成功");
                } else {
                    //退款状态为退款失败
                    refundStatus = RefundStatusEnum.REFUNDFAIL.value();
                    refundFailReason = transferRespDTO.getMessage();
                    result.add("订单号：[" + orderSn + "]，退款失败，" + refundFailReason);
                }

                refund.setRefundStatus(refundStatus);
                refund.setRefundFailReason(refundFailReason);
                //发送退款消息给consumer---》完成1.微信发送退款通知，2.修改退补状态和退补失败原因以及退款时间，
                //3.按照每个商品占订单总价比例扣减退补金额，4.调用【更新退款后剩余收益】方法修改各方收益
                this.amqpTemplate.convertAndSend(AmqpExchange.SPREAD_REFUND_COMPLETE,
                        AmqpExchange.SPREAD_REFUND_COMPLETE + "_ROUTING", refund);
            } catch (Exception e) {
                result.add("订单号：[" + orderSn + "]，退款失败，" + e.getMessage());
                logger.error("多退少补发起退款失败", e);
            }
        }
        return result;
    }
    //退款单确定退款账户类型
    private void getAccountType(RefundDO refund, PaymentMethodDO paymentMethodDO) {
        //原路退回WEIXINPAY  ALIPAY
        String pluginId = paymentMethodDO.getPluginId();
        String accountType = null;
        if (PaymentPluginEnum.weixinPayPlugin.toString().equals(pluginId)) {
            accountType = AccountTypeEnum.WEIXINPAY.value();
        }
        if (PaymentPluginEnum.alipayDirectPlugin.toString().equals(pluginId)) {
            accountType = AccountTypeEnum.ALIPAY.value();
        }
        if (PaymentPluginEnum.walletPayPlugin.toString().equals(pluginId)) {
            accountType = AccountTypeEnum.WALLETPAY.value();
        }
        if (PaymentPluginEnum.mergePayPlugin.toString().equals(pluginId)) {
            accountType = AccountTypeEnum.MERGEPAY.value();
        }
        refund.setAccountType(accountType);
    }

    /**
     * 初始化钱包转账请求DTO
     * @param refund 退款单
     * @param refundItem 退款单明细
     * @param clerk 店员
     * @return 钱包转账请求DTO
     */
    private TransferReqDTO buildTransferReqDTO(RefundDO refund, RefundItemDO refundItem, Clerk clerk){
        TransferReqDTO transferReqDTO = new TransferReqDTO();
        transferReqDTO.setTradeVoucherNo(refundItem.getRefundSn());
        //查询初始化平台账户信息
        AccountDTO platformAccountDTO = accountManager.queryPlatformAccount(com.dag.eagleshop.core.account.model.enums.AccountTypeEnum.PLATFORM.getIndex());
        transferReqDTO.setDraweeMemberId(platformAccountDTO.getMemberId());
        transferReqDTO.setDraweeMemberName(platformAccountDTO.getAccountTypeName());
        transferReqDTO.setDraweeAccountType(platformAccountDTO.getAccountType());

        //查询初始化收益账户信息
        Member member = memberManager.getModel(refund.getMemberId());
        String accountMemberId = member.getAccountMemberId();
        Assert.hasText(accountMemberId, "未找到买家的钱包账号");
        transferReqDTO.setPayeeMemberId(accountMemberId);
        transferReqDTO.setPayeeMemberName(member.getNickname());
        transferReqDTO.setPayeeAccountType(com.dag.eagleshop.core.account.model.enums.AccountTypeEnum.MEMBER_MASTER.getIndex());

        //初始化转账金额、内容、方式、交易主题
        transferReqDTO.setAmount(BigDecimal.valueOf(refundItem.getRefundPrice()));
        transferReqDTO.setTradeContent(refund.getSellerRemark());
        transferReqDTO.setTransferType(TransferTypeEnum.AT_ONCE.getIndex());
        transferReqDTO.setTradeSubject(TradeSubjectEnum.SPREAD_REFUND.description());

        //初始化操作人信息
        transferReqDTO.setOperatorId(clerk.getClerkId().toString());
        transferReqDTO.setOperatorName(clerk.getClerkName());

        return transferReqDTO;
    }

    /**
     * 订单有效性校验
     * @param orderDetailDTO 订单详细信息DTO
     * @param sellerId 卖家ID
     */
    private void checkOrderAvailability(OrderDetailDTO orderDetailDTO, Integer sellerId){
        //不存在的订单或者不属于当前店铺的订单进行校验
        if (orderDetailDTO == null || !sellerId.equals(orderDetailDTO.getSellerId())) {
            throw new ServiceException(AftersaleErrorCode.E604.name(),  AftersaleErrorCode.E604.describe());
        }
        //判断订单是否支付
        if(orderDetailDTO.getPayStatus().equals(PayStatusEnum.PAY_NO.value())){
            throw new ServiceException(AftersaleErrorCode.E601.name(),  AftersaleErrorCode.E601.describe() + ",此订单未支付");
        }
    }

    /**
     * 退补金额校验
     * @param allowRefundPrice 订单可退款金额
     * @param refundPrice 退补金额
     * @param payMoneyExcludeShippingPrice 支付金额（不含运费）
     */
    private void checkRefundPrice(double allowRefundPrice, Double refundPrice, Double payMoneyExcludeShippingPrice){
        //如果订单的可退款金额小于等于0或者订单的可退款金额小于实际退款金额refundPrice，那么不允许退款
        if (allowRefundPrice <= 0 || allowRefundPrice < refundPrice) {
            throw new ServiceException(AftersaleErrorCode.E609.name(), "可退款金额不足，请与平台联系解决");
        }
        //校验退款金额--如果订单的支付金额 - 运费后小于实际退款金额refundPrice，那么不允许退款
        if (payMoneyExcludeShippingPrice == null || payMoneyExcludeShippingPrice <= 0 || payMoneyExcludeShippingPrice < refundPrice) {
            throw new ServiceException(AftersaleErrorCode.E600.name(), AftersaleErrorCode.E600.describe() + "（不含运费）");
        }
    }
    /**
     * 校验并获取确切退补金额
     * @param fixRefund 固定退款金额
     * @param spreadRefundDTO 差价退款VO
     * @return 具体退款金额
     */
    private Double checkAndGetRefundPrice(Double fixRefund, SpreadRefundDTO spreadRefundDTO){
        if (fixRefund != null){
            return fixRefund;
        } else {
            Double minRefund = spreadRefundDTO.getMinRefund();
            Assert.isTrue(minRefund != null && minRefund > 0, "随机退款最小值不得为空且不得小于0");
            Double maxRefund = spreadRefundDTO.getMaxRefund();
            Assert.isTrue(maxRefund != null && maxRefund >= minRefund, "随机退款最大值不得为空且不得小于随机退款最小值");
            //获取m-n的随机数，包括mn
            return this.getRandomFromRange(minRefund, maxRefund);
        }
    }

    /**
     * 传入double取值方法返回随机数
     * @param minRefund 最小值
     * @param maxRefund 最大值
     * @return 随机数
     */
    private Double getRandomFromRange(Double minRefund, Double maxRefund){
        return BigDecimal.valueOf(Math.random()).multiply(BigDecimal.valueOf(maxRefund).subtract(BigDecimal.valueOf(minRefund))).add(BigDecimal.valueOf(minRefund))
                .setScale(2, RoundingMode.HALF_UP)
                .doubleValue();
    }

    @Override
    public Page<SpreadRefundQueryParamVO> querySpreadRefund(SpreadRefundQueryParamVO spreadRefundQueryParamVO) {
        Assert.hasText(spreadRefundQueryParamVO.getRefundType(), "退补类型必选");
        //select here,to_char(time, 'yyyy-mm-dd hh24:mi:ss') time from emp;
        String sql = "select id,sn,member_id,member_name,seller_id,seller_name,order_sn,refund_status,create_time,refund_price,refund_way,seller_remark, "
                .concat("refund_reason,refuse_reason,trade_sn,refund_type,pay_order_no,refuse_type,payment_type,refund_fail_reason ")
                .concat("from es_refund ");
        List<Object> termList = new ArrayList<>();
        List<String> sqlSplit = new ArrayList<>();

        Integer memberId = spreadRefundQueryParamVO.getMemberId();
        if (!ObjectUtils.isEmpty(memberId)) {
            sqlSplit.add(" member_id=? ");
            termList.add(memberId);
        }
        Integer sellerId = spreadRefundQueryParamVO.getSellerId();
        if (!ObjectUtils.isEmpty(sellerId)) {
            sqlSplit.add(" seller_id=? ");
            termList.add(sellerId);
        }
        String sn = spreadRefundQueryParamVO.getSn();
        if (StringUtil.notEmpty(sn)) {
            sqlSplit.add(" sn=? ");
            termList.add(sn);
        }
        String orderSn = spreadRefundQueryParamVO.getOrderSn();
        if (StringUtil.notEmpty(orderSn)) {
            sqlSplit.add(" order_sn=? ");
            termList.add(orderSn);
        }
        String refundType = spreadRefundQueryParamVO.getRefundType();
        if (StringUtil.notEmpty(refundType)) {
            sqlSplit.add(" refund_type=? ");
            termList.add(RefundTypeEnum.valueOf(refundType).value());
        }
        String refundReason = spreadRefundQueryParamVO.getRefundReason();
        if (StringUtil.notEmpty(refundReason)) {
            sqlSplit.add(" refund_reason=? ");
            termList.add(refundReason);
        }
        String refundStatus = spreadRefundQueryParamVO.getRefundStatus();
        if (StringUtil.notEmpty(refundStatus)) {
            sqlSplit.add(" refund_status=? ");
            termList.add(refundStatus);
        }
        String operator = spreadRefundQueryParamVO.getOperator();
        if (StringUtil.notEmpty(operator)) {
            sqlSplit.add(" seller_name=? ");
            termList.add(operator);
        }
        //对sql条件语句进行拼接
        String sqlSplicing = SqlSplicingUtil.sqlSplicing(sqlSplit);
        if (!StringUtil.isEmpty(sqlSplicing)) {
            sql = sql.concat(sqlSplicing);
        }
        sql = sql.concat(" ORDER BY create_time desc");
        Page page = this.daoSupport.queryForPage(sql, spreadRefundQueryParamVO.getPageNo(),
                spreadRefundQueryParamVO.getPageSize(), RefundDO.class, termList.toArray());
        List<RefundDO> refundList = page.getData();
        List<SpreadRefundQueryParamVO> spreadRefundQueryParamVOS = new ArrayList<>();
        for (RefundDO refund : refundList) {
            SpreadRefundQueryParamVO spreadParam = new SpreadRefundQueryParamVO();
            BeanUtil.copyProperties(refund, spreadParam);
            if (!ObjectUtils.isEmpty(sellerId)) {
                //商家查询后需要的属性赋值
                spreadParam = this.sellerAttributeAssignment(spreadParam, refund);
            } else {
                //商家和用户查询后共同需要属性赋值
                spreadParam = this.commonAttributeAssignment(spreadParam, refund);
            }
            spreadRefundQueryParamVOS.add(spreadParam);
        }
        page.setData(spreadRefundQueryParamVOS);
        return page;
    }

    /**
     * RefundDO转SpreadRefundQueryParam时商家查询后需要的属性赋值
     * @param spreadParam 退补VO
     * @param refund 退款单
     * @return 退补原因、订单号、退补金额、退补描述、申请售后类型、退补状态、操作人、退补标题、退补说明
     */
    private SpreadRefundQueryParamVO sellerAttributeAssignment(SpreadRefundQueryParamVO spreadParam, RefundDO refund) {
        //调用商家和用户查询后共同需要属性赋值
        spreadParam = this.commonAttributeAssignment(spreadParam, refund);
        spreadParam.setRefundType(RefundTypeEnum.valueOf(spreadParam.getRefundType()).description());
        spreadParam.setRefundStatus(spreadParam.getRefundStatus());
        spreadParam.setOperator(refund.getSellerName());
        spreadParam.setRefundTitle("订单" + spreadParam.getRefundReason() + "产生退款");
        //退款成功的才会有退款说明
        if(RefundStatusEnum.COMPLETED.value().equals(refund.getRefundStatus())){
            spreadParam.setRefundIntroductions("您的订单"+ refund.getSn() + "中的商品，因为"
                    + spreadParam.getRefundReason() + "产生了退款共计" + refund.getRefundPrice() + "元，款项已原路退回");
        }
        return spreadParam;
    }

    /**
     * RefundDO转SpreadRefundQueryParam时商家和用户查询后共同需要属性赋值
     * @param spreadParam 退补VO
     * @param refund 退款单
     * @return 退补原因、订单号、退补金额、退补描述
     */
    private SpreadRefundQueryParamVO commonAttributeAssignment(SpreadRefundQueryParamVO spreadParam, RefundDO refund) {
        String refundReasonString = RefundReasonEnum.valueOf(spreadParam.getRefundReason()).description();
        spreadParam.setRefundReason(refundReasonString);
        spreadParam.setRefundDescription(refund.getSellerRemark());
        spreadParam.setRefundTime(DateUtil.toString(refund.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
        return spreadParam;
    }
}
